package main

import (
	"fmt"
	"sync/atomic"
	"time"
)

/*
	问题 1：怎样才能让主 goroutine 等待其他 goroutine？

	最简单粗暴的办法就是让主 goroutine“小睡”一会儿。
	for i := 0; i < 10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
	time.Sleep(time.Millisecond * 500)

	这个办法是可行的，只要“睡眠”的时间不要太短就好。不过，问题恰恰就在这里，
	让主 goroutine“睡眠”多长时间才是合适的呢？如果“睡眠”太短，则很可能不足以让其他的 goroutine 运行完毕，
	而若“睡眠”太长则纯属浪费时间，这个时间就太难把握了。

	使用sync.WaitGroup类型

问题 2：怎样让我们启用的多个 goroutine 按照既定的顺序运行？

 */

func main() {

	for i:=0;i< 10; i++ {
		go func() {
			fmt.Println(i)
		}()
	}
	time.Sleep(time.Microsecond * 500)

	fmt.Println("-------------------------------------")

	for i:=uint32(0);i< 10; i++ {
		go func(i uint32) {
			{
				fn := func() {
					fmt.Println(i)
				}
				trigger(i, fn)
			}
		}(i)
	}

	trigger(10, func() {})

}

var count  = uint32(0)

/*
	trigger函数会不断地获取一个名叫count的变量的值，并判断该值是否与参数i的值相同。
	如果相同，那么就立即调用fn代表的函数，然后把count变量的值加1，最后显式地退出当前的循环。
	否则，就先让当前的 goroutine“睡眠”一个纳秒再进入下一个迭代。

	类似于go实现的自旋锁的思想
 */
func trigger(i uint32, fn func()) {
	for {
		if n := atomic.LoadUint32(&count); n == i {
			fn()
			atomic.AddUint32(&count, 1)
			break
		}
		time.Sleep(time.Nanosecond)
	}
}